#include <string.h>
#include "gkt_debug.h"
#include "gkt_os.h"
#include "gkt_event.h"

#ifdef GKT_CONFIG_EVENT_MQUEUE_MAXSIZE
#define EVENT_MQUEUE_MAXSIZE	GKT_CONFIG_EVENT_MQUEUE_MAXSIZE
#else
#define EVENT_MQUEUE_MAXSIZE	8
#endif

typedef struct _event {
	const gkt_event_desc_s	*descs[GKT_EVENT_MAX];

	gkt_mqueue_t	mqueue;
	volatile uint32_t	mask_bits;

	int	event_staged;
	gkt_event_s	staged_event;
} event_s;
static event_s s_event;

int gkt_event_put(const gkt_event_s *event, uint32_t timeout)
{
	uint32_t mask;
	int retval;

	GKT_ASSERT(s_event.mqueue);

	if (__LIKELY(event)) {
		mask = 1U << event->major_type;
		if (__LIKELY(!(s_event.mask_bits & mask))
			|| (event->flags & GKT_EVENT_F_BYPASS)) 
		{
			retval = gkt_mqueue_put(s_event.mqueue, (const void *)event, timeout);
			if (__UNLIKELY(retval != GKT_SUCCESS)) {
				gkt_warning("event: put failed(%d)\n", retval);
				gkt_warning("event: window_id(%d) type(%u - %u)\n", event->window_id, 
					event->major_type, event->minor_type);
			}
		}
		else
			retval = GKT_EPERMIT;
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

void gkt_event_stage(const gkt_event_s *event)
{
	if (__LIKELY(event)) {
		memcpy(&s_event.staged_event, event, sizeof(gkt_event_s));
		s_event.event_staged = 1;
	}
}

int gkt_event_get(gkt_event_s *event, uint32_t timeout)
{
	uint32_t mask;
	int retval;

	GKT_ASSERT(s_event.mqueue);
	GKT_ASSERT(event);

	if (s_event.event_staged) {
		memcpy(event, &s_event.staged_event, sizeof(gkt_event_s));
		s_event.event_staged = 0;

		event->flags |= GKT_EVENT_F_STAGED;		
		retval = GKT_SUCCESS;
	}
	else {
mqueue_get:
		retval = gkt_mqueue_get(s_event.mqueue, (void *)event, timeout);
		if (GKT_SUCCESS == retval) {
			GKT_ASSERT(event->major_type < GKT_EVENT_MAX);
			mask = 1U << event->major_type;
			if (__UNLIKELY(s_event.mask_bits & mask) 
				&& !(event->flags & GKT_EVENT_F_BYPASS)) 
			{
				gkt_trace("event_get: masked event - %u\n", event->major_type);
				goto mqueue_get;
			}

			event->flags &= ~GKT_EVENT_F_STAGED;
		}
	}

	return retval;
}

void gkt_event_clear(void)
{
	gkt_event_s event;
	int retval;

	do {
		retval = gkt_mqueue_get(s_event.mqueue, 
					(void *)&event, GKT_IMMEDIATELY);
	} while (GKT_SUCCESS == retval);
}

#if 0
void gkt_event_mask(uint32_t mask_bits)
{
	uint32_t major_type, action_bits, mask;
	int masked;

	action_bits = s_event.mask_bits ^ mask_bits;
	s_event.mask_bits = mask_bits;

	major_type = GKT_EVENT_MASKABLE;
	while (major_type < GKT_EVENT_MAX) {
		mask = 1U << major_type;
		if ((action_bits & mask)
			&& s_event.descs[major_type] 
			&& s_event.descs[major_type]->mask)
		{
			masked = (mask_bits & mask) ? 1 : 0;
			(*s_event.descs[major_type]->mask)(masked, 0);
		}

		major_type++;
	}
}
#else
void gkt_event_mask(uint32_t mask_bits)
{
	uint32_t major_type, real_mask_bits, mask;

	real_mask_bits = mask_bits & GKT_EVENT_MASKABLE_BITS;
	if (!real_mask_bits)
		return;
	
	major_type = GKT_EVENT_MASKABLE;
	while (major_type < GKT_EVENT_MAX) {
		mask = 1U << major_type;
		if ((real_mask_bits & mask)
			&& !(s_event.mask_bits & mask))
		{
			s_event.mask_bits |= mask;
			if (s_event.descs[major_type] 
				&& s_event.descs[major_type]->mask)
			{
				(*s_event.descs[major_type]->mask)(1, 0);
			}
		}

		major_type++;
	}
}

void gkt_event_unmask(uint32_t unmask_bits)
{
	uint32_t major_type, real_unmask_bits, mask;

	real_unmask_bits = unmask_bits & GKT_EVENT_MASKABLE_BITS;
	if (!real_unmask_bits)
		return;
	
	major_type = GKT_EVENT_MASKABLE;
	while (major_type < GKT_EVENT_MAX) {
		mask = 1U << major_type;
		if ((real_unmask_bits & mask)
			&& (s_event.mask_bits & mask))
		{
			s_event.mask_bits &= ~mask;
			if (s_event.descs[major_type] 
				&& s_event.descs[major_type]->mask)
			{
				(*s_event.descs[major_type]->mask)(0, 0);
			}
		}

		major_type++;
	}
}
#endif

void gkt_event_mask_single(uint32_t major_type, uint32_t flags)
{
	uint32_t mask, masked;

	if (major_type < GKT_EVENT_MAX) {
		mask = 1U << major_type;
		masked = s_event.mask_bits & mask;
		s_event.mask_bits |= mask;

		if (!masked
			&& s_event.descs[major_type] 
			&& s_event.descs[major_type]->mask)
		{
			(*s_event.descs[major_type]->mask)(1, flags);
		}
	}
}

void gkt_event_unmask_single(uint32_t major_type)
{
	uint32_t mask, masked;

	if (major_type < GKT_EVENT_MAX) {
		mask = 1U << major_type;
		masked = s_event.mask_bits & mask;
		s_event.mask_bits &= ~mask;

		if (masked
			&& s_event.descs[major_type] 
			&& s_event.descs[major_type]->mask)
		{
			(*s_event.descs[major_type]->mask)(0, 0);
		}
	}
}

void gkt_event_unmask_single_flags(uint32_t major_type, uint32_t flags)
{
	uint32_t mask, masked;

	if (major_type < GKT_EVENT_MAX) {
		mask = 1U << major_type;
		masked = s_event.mask_bits & mask;
		s_event.mask_bits &= ~mask;

		if (masked
			&& s_event.descs[major_type] 
			&& s_event.descs[major_type]->mask)
		{
			(*s_event.descs[major_type]->mask)(0, flags);
		}
	}
}

int gkt_event_is_masked(uint32_t major_type)
{
	uint32_t mask = 1U << major_type;
	return (s_event.mask_bits & mask) ? 1 : 0;
}

gkt_event_handler gkt_event_get_handler(uint32_t major_type)
{
	if ((major_type < GKT_EVENT_MAX) 
		&& s_event.descs[major_type])
	{
		return s_event.descs[major_type]->handler;
	}

	return NULL;
}

extern uint32_t __event_descs_start;
extern uint32_t __event_descs_end;
int gkt_event_init(void)
{
	const gkt_event_desc_s *event_desc;
	const gkt_event_desc_s *event_descs_start;
	const gkt_event_desc_s *event_descs_end;
	uint32_t major_type;
	int retval;

	s_event.mqueue = gkt_mqueue_create(EVENT_MQUEUE_MAXSIZE, 
				sizeof(gkt_event_s));
	if (__UNLIKELY(!s_event.mqueue)) {
		gkt_error("event_init: create mqueue failed.\n");
		return GKT_ENOMEMORY;
	}

	event_descs_start = (const gkt_event_desc_s *)&__event_descs_start;
	event_descs_end = (const gkt_event_desc_s *)&__event_descs_end;

	event_desc = event_descs_start;
	while (event_desc < event_descs_end) {
		if (event_desc->major_type < GKT_EVENT_MAX)
			s_event.descs[event_desc->major_type] = event_desc;
		event_desc++;
	}

	major_type = 0;
	while (major_type < GKT_EVENT_MAX) {
		if (major_type >= GKT_EVENT_MASKABLE)
			s_event.mask_bits |= 1U << major_type;

		if (s_event.descs[major_type]
			&& s_event.descs[major_type]->init) 
		{
			retval = (*s_event.descs[major_type]->init)();
			if (__UNLIKELY(retval != GKT_SUCCESS))
				gkt_error("event_init: (%u) failed %d\n", major_type, retval);
		}

		major_type++;
	}

	return GKT_SUCCESS;
}

